実用 Go言語
どんな本
Goを業務で使っていこうとしている初心者が中級者になるためにGoらしく書いたり運用したりするためのTips集
感想
Goは慣れない文法はありつつもシンプルである。その分コードを書き進めていく際にGolangの定石や設計パターンをどれだけ知っているかが上達するために重要。この本は実際の業務でありがちが要件や悩みどころに対して、その辺りのGolangの定石や設計パターンはどうなっているのかというのを解説してくれている点がとても良い。困ったときに対象となる章を開いて知見を得るというような使い方もできそうなので初心者が手元に置いておくと便利そうな一冊。
ただJavaとの比較が多いのでJavaを知らないと知らんがなとなる
個人的にはPHP/Ruby/JS(仕事) -> Golang(個人開発) -> TS/Dart(仕事) -> Rust(個人開発)と変遷してきたのだが、今またGolangに戻ってきて感じるのは手に馴染む必要十分なツールだなということ。
DartでFlutter開発していた時に困ったらソース読めばいいか〜となるくらい標準ライブラリのコードが綺麗でドキュメントが多くすぐジャンプしてなんでも追えた経験が結構好きだった。あれに近いものをGolangにも感じる。
PHP/Ruby/JSを触っていた初期にGolangへ入門した当時は静的型にそもそも慣れてないし書きにくいなと思っていたが、その後いくつもの言語を使い、型にもシステムプログラミングの肌感もある程度知った今、Golangは楽にかけて良い...という気持ちになる。文法的にも機能的にも少ないから記憶力が低下した老化した脳への負担が少ないのも良い。
Rustも所有権とか関数型的な考え方とかそもそもシステムプログラミング全般の実装を学ぶときに得るものが大きかったが、自分は所詮Webやアプリ開発を行う仕事が主なのでGolangで十分なことが多い。
当時使っていた頃はv1.8~1.12くらいでまだモジュール周りのセオリーが整ってないような時期だったけど今は採用している会社も増えているしエコシステムもより成熟した印象を受ける。
実際の業務開発で積み重ねられた経験に裏打ちされた実用本が発売されるほどには各所で使われているということだ思うのでこれを機にまたGolang方面を主戦場としていくのも良さそうと感じた。
メモ
Goは1.12とかGoModuleが導入されるかされないかあたりまでは個人開発で使っていたが、その後使うことがなくなった。この本では1.18に対応しているので知らない文法や用例、その他決まり事などが増えていそう。
まずは付録Aと付録Bを読んで文法の駆け足おさらいと最新情報を得るための情報源をキャッチアップする
付録A: 駆け足で学ぶGoの基礎
20ページくらいでおさらいできて便利。が、reflection/Contextあたりは省かれてるので別途復習しないとだめっぽい。Genericsは3,4章で、goroutineは16章で触れられている。
Contextはスレッドローカルストレージ的な使い方ができるやつ
付録B: Goの最新情報を知るための情報源
GoDocの読み方
気になる章をつまみ食いしていくか
1.6 関数のオプション引数
構造体パターン > ビルダー形式 | Functional Option パターン
関数定義のセオリーを知ることは野良のpackageのインターフェースを把握しやすくなることにつながる
他のライブラリのコードやGoDocを読む時にパターンを知っておくと認知負荷が減るので良い
1.7 プログラムを制御する引数
基本はflagパッケージで十分。ちゃんとCLIとして整えるなら別だけど。
1.8 メモリ起因のパフォーマンス低下を解消する
スライス/マップのメモリ確保が主因
メモリ確保の流れ
すでに確保済みのメモリが不足した場合は、OSに要求する
確保済みのものから新しい配列用にメモリを割り当てる
新しい配列に古い配列の内容をコピー
古い配列にアクセスするスライスや変数がなくなったらメモリを解放
ユーザー空間ではなくOSの処理に移行 -> 遅い
最大量がわかっている場合はcapacity指定をする
計測重要
どうやって計測するのがメジャーなのだろうか?
deferはreturnでスコープを抜けるときに実行されるのでforなどの中ではループを抜けるまでdeferが実行されない -> リソース確保が解けない
1.9 文字列の結合
+での結合が遅い理由 -> Goの文字列は不変 == 上記の問題と一緒でメモリが都度新しく確保されてしまうので
strings.Buiderを使う。Growを使うと最大文字数を指定できる。
ひとまとめにしちゃうのもあり -> str := "a" + "b" + "c"みたいな
2章 定義型
なるべくレシーバーとして実装することが、Go らしいプ ログラミングの方法
fmtの拡張で秘匿情報のログ漏れ対策良い
Stringer/GoStringerを定義するだけ
この二つの方の違いは?
前者は%sや%v、後者は%#v(Goの構文で出力される。例えば構造体であれば構造体のフィールド名まで表示される)のフォーマットに使われる
3章 構造体
ファクトリ関数はどのpackageでも普通に使われてるからstructの初期化の標準機能だと思ってたけど慣習だった
確かにゼロ値での初期化は注意が必要で暗黙知が生まれやすそう。ファクトリを使う慣習の方が事故が減りそうではある。
ジェネリクスの解説ここにあった。Go1.18に対応してるが付録Aからは省かれてるから無いのかと思った
構造体設計のポイント
値ではなくポインタとして扱う
値として扱うとフィールドにmapとかスライスを使えないのでそもそも不便
が、値として扱えばスタックにメモリ確保されるだけなのでヒープを使わずGCの手間を減らせる
ValueObjectt系なものは値として扱い、Entityは構造体でミュータブルに扱うのがおすすめっぽい。全部イミュータブルにしたくはなるが...
ゼロ値の初期化をデフォルトのままにするのか別途初期化処理を施すのかは場合による
「ポインターで扱う前提」「ミュータブルな API セット を提供」「特定のファクトリー関数でのみ動作(ゼロ値動作を保証しない)」が、いちばんお手軽
空の構造体
メモリを占有しないのでゼロバイトで使いまわせるのか、なるほど。
チャネルのキャッチボールとかで使われたりする
インターフェース(抽象型)と具体的な実装を分ける。インスタンス化するときに具体的な実装だけ差し替えられるようにする。
4章 インターフェース
シンプルなインタフェースを使う API をコアとして作り、それをラップして使いやすい API を別に提供するのが、標準ライブラリを含め Go で広く行われている設計手法
クラウドサービスをローカルでも再現するやつ。ローカルと本番環境を同じにしてテストするのに使える。
へぇ〜
テストを考えてすべ てモック化できるようにインタフェースにしておく、という考えをしている Go ユーザーは少数派です。
ストラテジーパターンとしてDI的に差し替える実装の方が手軽そう
実装の都合であればインタフェースにはしない、利用者の利便性のため のインタフェースならあり
5章 エラーハンドリング
エラーに含まれる文字列を特定の文字列と比較する方法はアンチパターン
どの言語でも一緒
exponential backoff
log.Fatalを使うとプログラムが即座に終了してdeferでリソースが解放できない。レビュー時に気を付ける。
errのチェック忘れ防止
6章 パッケージ・モジュール
最新のセオリーを知っておきたいので有益。ネットの情報だとばらつきが結構あるので。
パッケージ
フォルダ = パッケージ
モジュール ⊇ パッケージ
モジュールとパッケージ
モジュール: https://github.com/myname/myproject
パッケージ: myproject
ライブラリ管理
Go Module使え
GOPATHは過去の遺物になった
パッケージ構成について
パッケージを分けたくなるほど複雑な場合はリポジトリを分ける方が良い
共通循環参照だけは避けたい。
パッケージは出来るだけ分けず、ルートに共通部分のロジックを集め、そこをutils置き場とする
proxy.golang.orgなどのミラーがあるのでgithubの大元のリポジトリが削除されてもDL自体はできる。vender不要になってるらしい。
プラグイン機構
知らん概念だった。
標準のpluginパッケージはあんまり使われてないらしい。import時にinitが呼ばれる(ブランクインポート)を応用して、initをもつpluginを作り、実行側でそのpackageをimport _ "someplugin/plugins/a"のように読み込み、必要なpackageのみpluginとして使うみたいなやり方が紹介されている。 その他
7章 Goプログラミングの環境を整備する
VSCodeの便利機能一覧のところ全部覚えたい
よく使われるビルドフラグ
CGO_ENABLED=0 go build -trimpath -ldflags '-s -w -X main.version=1.0.0' main.go
cgoが必要な時は1にする
特定のCPU向けにビルドする場合はこんな感じの環境変数も足す
GOOS=linuxとGOARCH=amd64
以降8~16章は各種ユースケースごとで使える話題が多め。サラッと目を通しつつ近々で使いそうとか興味のあるところをかいつまんで読んでいく(とはいえここからが本番感がある...)
8章 さまざまなデータフォーマット
JSONをGoの構造体に変換してくれるやつ
json.RawMessageを使って動的に変わるjsonのフィールドを遅延評価できるようにしておく
固定長フォーマットを扱うという経験が無いんだけどどこで使われてるんだ...
9章 Goとリレーショナルデータベース
ORM使うのか?とマイグレーションどう管理してるのか?だけ気になる
テストは出来るだけ本物のデータベースを使う
ORM
マイグレーションの管理の話は出てこなかった...。上記ORMには機能的には存在しているようだ。
10章 HTTPサーバー
ミドルウェア例
APIドキュメント生成
11章 HTTPクライアント
特に気になったところなし
12章 ログとオブザーバビリティ
13章 テスト
Table Driven Test
標準のtestingパッケージで十分。あとはノウハウ。
$ go test -run Model
特定のワードが含まれるテストだけ実行
$ go test -short
時間のかかるテストをスキップしてくれる
reflect.DeepEqualの強化版
Exampleへの追加
14章 クラウドとGo
15章 クラウドのストレージ
オブジェクトストレージ版、database/sqlのようなベンダによらず統一的に操作できるインターフェースで扱えるようにするライブラリ
16章エンタープライズなGoアプリケーションと並行処理
主に無限ループ起因のゴルーチンリークは気をつける
リソース
リリースノート
Goらしい書き方を学ぶためのドキュメント
標準ライブラリ読むのが良い
メルカリの人のGolangの基本文法解説
ジェネリクス